/**
 * 
 */
package pl.edu.agh.winmon.widget;

import java.text.NumberFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.event.ChartChangeEvent;
import org.jfree.chart.event.ChartChangeEventType;
import org.jfree.chart.event.ChartChangeListener;
import org.jfree.data.time.DateRange;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.experimental.chart.swt.ChartComposite;

import pl.edu.agh.winmon.analise.MemoryUsageSeriesProvider;
import pl.edu.agh.winmon.hibernate.MemoryStatEntry;
import pl.edu.agh.winmon.hibernate.Session;
import pl.edu.agh.winmon.hibernate.Turn;

/**
 * This widget provides memory info.
 * 
 * @author kret
 */
public class MemoryInfoWidget extends SessionUpdatableComposite {

	private TimePickerWidget timePickerWidget;

	private MemoryUsageSeriesProvider memoryUsageSeriesProvider = new MemoryUsageSeriesProvider();

	private Session session;

	private Combo screenshotCombo;

	private Label totalValue;

	private Label ramValue;

	private Label usedValue;

	private Label usedPercValue;

	private Label freeValue;

	private Label freePercValue;

	private ChartComposite chartComposite;

	private Group chartGroup;

	private NumberFormat nf;

	// private Label

	private Map<String, MemoryStatEntry> mems = new HashMap<String, MemoryStatEntry>();

	/**
	 * Constructor.
	 * 
	 * @param parent
	 *            parent composite
	 */
	public MemoryInfoWidget(Composite parent) {
		super(parent, SWT.NONE);
		nf = NumberFormat.getInstance();
		nf.setMaximumFractionDigits(2);
		setLayout(new FillLayout(SWT.VERTICAL));
		createControls();
	}

	private void createScreenshotPicker(Composite parent) {
		Label screenshotDate = new Label(parent, SWT.NONE);
		screenshotDate.setText("Choose system screenshot");

		screenshotCombo = new Combo(parent, SWT.NONE);
		screenshotCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		screenshotCombo.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				updateScreenshotSelection();
			}
		});
	}

	private void createMemoryInfoGroup() {
		// creating memory group
		Group memGroup = new Group(this, SWT.NONE);
		memGroup.setLayout(new GridLayout(2, true));
		memGroup.setText("Memory info");

		createScreenshotPicker(memGroup);

		Label label = new Label(memGroup, SWT.NONE);
		label.setText("Total memory (MB)");

		totalValue = new Label(memGroup, SWT.NONE);
		totalValue.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		label = new Label(memGroup, SWT.NONE);
		label.setText("RAM memory (MB)");

		ramValue = new Label(memGroup, SWT.NONE);
		ramValue.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		label = new Label(memGroup, SWT.NONE);
		label.setText("Used memory (MB)");

		usedValue = new Label(memGroup, SWT.NONE);
		usedValue.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		label = new Label(memGroup, SWT.NONE);
		label.setText("Used memory percentage (%)");

		usedPercValue = new Label(memGroup, SWT.NONE);
		usedPercValue.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		label = new Label(memGroup, SWT.NONE);
		label.setText("Free memory (MB)");

		freeValue = new Label(memGroup, SWT.NONE);
		freeValue.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		label = new Label(memGroup, SWT.NONE);
		label.setText("Free memory percentage (%)");

		freePercValue = new Label(memGroup, SWT.NONE);
		freePercValue.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

	}

	private void createChartGroup() {
		// Creating chart group.
		chartGroup = new Group(this, SWT.NONE);
		chartGroup.setLayout(new GridLayout(1, false));
		chartGroup.setText("Memory charts");

		timePickerWidget = new TimePickerWidget(chartGroup);
		timePickerWidget.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				updateChart();
			}
		});
	}

	private void updateChart() {

		TimeSeriesCollection xyDataset = memoryUsageSeriesProvider
				.provideSeries(session, timePickerWidget.getStartDate(),
						timePickerWidget.getEndDate());

		JFreeChart chart = ChartFactory.createTimeSeriesChart(
				"Average memory usage", // Title
				"Time", // X-Axis label
				"Memory usage", // Y-Axis label
				xyDataset, // Dataset
				false, // Show legend
				false, // Show tooltip
				false);

		if (chartComposite == null) {
			chartComposite = new ChartComposite(chartGroup, SWT.BORDER, chart);
			GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
			// gridData.horizontalSpan = 3;

			chartComposite.setLayoutData(gridData);
			chartGroup.layout();
		} else {
			chartComposite.setChart(chart);
			chartComposite.redraw();
			chartComposite.getParent().layout();
		}
		chartComposite.getChart().addChangeListener(new ChartChangeListener() {

			@Override
			public void chartChanged(ChartChangeEvent arg0) {
				if (arg0.getChart() != null)
					if (arg0.getType().equals(ChartChangeEventType.GENERAL)) {
						timePickerWidget.getStartTimeCombo().setDate(
								((DateRange) arg0.getChart().getXYPlot()
										.getDomainAxis().getRange())
										.getLowerDate());
						timePickerWidget.getEndTimeCombo().setDate(
								((DateRange) arg0.getChart().getXYPlot()
										.getDomainAxis().getRange())
										.getUpperDate());
					}
			}
		});
	}

	private void createControls() {
		createMemoryInfoGroup();
		createChartGroup();
	}

	private void updateScreenshotSelection() {

		MemoryStatEntry memoryStatEntry = mems.get(screenshotCombo.getText());
		if (memoryStatEntry != null) {
			usedValue.setText(memoryStatEntry.getUsed() / (1024 * 1000) + "");
			usedPercValue.setText(nf.format(memoryStatEntry.getUsedPercent()));

			freeValue.setText(memoryStatEntry.getFree() / (1024 * 1000) + "");
			freePercValue.setText(nf.format(memoryStatEntry.getFreePercent()));
		}
	}

	@Override
	public void updateOnSessionChange(Session session) {
		this.session = session;
		screenshotCombo.removeAll();
		mems.clear();
		for (Turn turn : session.getTurns()) {
			if (turn != null) {
				mems.put(new Date(turn.getStartTime()).toString(),
						turn.getMemoryStatEntry());
				screenshotCombo.add(new Date(turn.getStartTime()).toString());
			}
		}

		totalValue.setText(""
				+ (session.getMemoryInfo().getTotalMemory() / (1000 * 1024)));
		ramValue.setText("" + (session.getMemoryInfo().getRamMemory()));

		screenshotCombo.select(0);
		updateScreenshotSelection();
		timePickerWidget.setSession(session);
		updateChart();
	}
}
